Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New batcher payment primitive #421

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

SebastienGllmt
Copy link
Contributor

Right now the batcher system in Paima is a bit too simple. It only allows paying for user's tx fees up to a max per day and that's it

This is good enough for networks like Xai where the costs are low, but not sufficient for other networks

To handle more expensive EVM chains, we need a way for users to "refill" a balance for the batcher to pay on their behalf

Conceptually, how do we implement this?

Part 1: indexing the payments

A tempting solution is to say "okay, let's just index all the txs sent to the batcher to know the user's balance"

However, this isn't an option for EVM because transaction history for an address is not part of the EVM RPC standard. Instead, all we have access to are logs

Therefore, we create a Solidity smart contract that receives payment for the batcher and emits an event that you can then index!

Part 2: where to index this

Ideally, the indexing of this data does not happen in the batcher itself. This is because right now the batcher is mostly stateless - you can wipe the batcher entirely and redeploy it, and it will just work. Having the batcher have to keep track of user balances would increase the fragility of the batcher, and duplicate a lot of stuff we implemented to solve exactly this problem for Paima Engine!

Solution: have Paima Engine index the batcher payments! This means that there is a single contract that every batcher reuses, and Paima monitors it

Batcher primitive

The way this will be implemented in practice is with a new batcher primitive that does a few things:

  1. It manages a new Postgres table mapping userID to balance
  2. It indexes the new BatcherPayment contract to increment the user balance in the table
  3. When a new batch gets posted, it looks at which batcher made the transactions, and reduces the balance of all the addresses included in the batch depending on their percentage of the batch size (ex: if a user is 50% of the batch, their balance is deducted by 50% of the tx fee of posting that batch)

Tricky points

The main problem with this approach is there is a delay between the batcher submitting a transaction to the blockchain and Paima Engine receiving it (and updating the balances). That means that, in a naive implementation, an attacker could make many txs very fast before Paima Engine has time to let the batcher know the cost of these transactions

To solve this, we can leverage the new MQTT system we have to subtract the estimated cost from the user's balance in-memory in the batcher, and remove this in-memory tracking once we receive an update from Paima that the tx has been included

What about this system vs ERC-4337 (the EVM account abstraction standard)

ERC-4337 has a lot of issues in practice

  • It has a very expensive gas cost
  • It's not supported on every chain (we'd have to deploy a bunch of stuff ourselves)
  • It requires specialized wallets to use (not supported by MetaMask)
  • Depends on a reputation system for batchers (which we won't have on most chains)
  • It's not really optimized for batching stuff together like our batcher is (not as focused on tx fee reduction)
  • EVM-specific (whereas our batcher supports Avail and more chains in the future)
  • Requires an initial tx to start using (it requires users to deploy smart contracts to start using)

Compared to this, our approach is conceptually simple.

Future for our batcher?

The current solution, although it works, does have some limitations:

  • It can't support non-native tokens (how would you map the ERC20 value to the native token cost? It would require some kind of oracle which adds a lot of complexity, and most gaming chains have no notable ERC20 let alone oracles
  • Both native token price and gas price changes over time, which can cause UX confusion (a user may think they're paying for 100 free txs, but by the time they submit these txs the gas prices have changed and maybe they only get 50)
  • Requires trusting the batcher not to walk away with the funds (i.e. people shouldn't be putting large amounts of money into this at once)
  • Hard to subsidize based on custom logic (ex: holds an NFT, 5 free txs per day, etc.)

I think we can tackle at least the custom logic side of things by allowing the batcher to specify which txs in a batch are subsidized so they don't affect the logic (by adding a new field in the per-tx info in the batcher tx format)

Ideally, people running batchers could specify custom logic for how to subsidize txs. We can, of course, continue to do it like we do now (where we hardcode these subsidy options as batcher env configurations), but in the future we could consider an NPM package people can use to download a batcher they can write custom logic for (but this is not a priority for now)

* Batcher CDE

* Added Batcher Support for Fees

* Only allow one Batcher

* Merged Batcher Contract into PaimaL2Contract

* Keeping Batcher temporal fees in memory. Removed ENV BATCHER_PAYMENT_ENABLED.

* Update Batcher Balance

* added notice to batcher payments writes

* Removed wei reference
@SebastienGllmt
Copy link
Contributor Author

@acedward can you help rebase this?

msg: string,
hashes: string[]
): Promise<[number, string]> => {
const { txRequest, serializedSignedTx } = await this.buildTransaction(msg);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.provider.finalizeTransaction actually makes network calls, so it's best not to repeat this work if possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants